/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include <trace_log.h>
#include "umc_avs_enc_vlc.h"
#include "umc_avs_enc_bit_stream.h"
#include "umc_avs_dec_bit_stream.h"

namespace UMC
{

static
Ipp32s AVSEncScan[2][64] =
{
    {
        0,  1,  8,  16, 9,  2,  3,  10,
        17, 24, 32, 25, 18, 11, 4,  5,
        12, 19, 26, 33, 40, 48, 41, 34,
        27, 20, 13, 6,  7,  14, 21, 28,
        35, 42, 49, 56, 57, 50, 43, 36,
        29, 22, 15, 23, 30, 37, 44, 51,
        58, 59, 52, 45, 38, 31, 39, 46,
        53, 60, 61, 54, 47, 55, 62, 63
    },

    {
        0,  8,  16, 1,  24, 32, 9,  17,
        40, 48, 25, 2,  10, 56, 33, 18,
        3,  41, 49, 26, 11, 19, 4,  57,
        34, 12, 42, 27, 20, 50, 35, 28,
        5,  13, 58, 43, 36, 21, 6,  29,
        51, 44, 14, 22, 37, 59, 52, 30,
        45, 60, 38, 53, 46, 61, 54, 7,
        62, 15, 23, 31, 39, 47, 55, 63
    }
};

IppStatus avs_EncodeLumaBlockIntra_AVS_1u16s(Ipp32u **ppBitStream,
                                             Ipp32s *pBitOffset,
                                             const Ipp16s *pDstCoeffs,
                                             Ipp32u scanType)
{
    Ipp32u numVLCCodes;
    Ipp32s runArray[64];
    Ipp32s levelArray[64];
    AVS_BIT_STREAM_CONTEXT ctx;

    // initialize bit stream
    ctx.dst = *ppBitStream;
    ctx.offset = *pBitOffset;

    //
    //  reorder levels
    //

    {
        Ipp32s *pScan = AVSEncScan[scanType];
        Ipp32s iIndex = 0;
        Ipp32u run;

        run = 0;
        numVLCCodes = 0;
        for (iIndex = 0; iIndex < 64; iIndex += 1)
        {
            Ipp32s level;

            // get current amplitude
            level = pDstCoeffs[pScan[iIndex]];
            if (level)
            {
                runArray[numVLCCodes] = run;
                levelArray[numVLCCodes] = level;
                numVLCCodes += 1;

                // reset run
                run = 0;
            }
            else
            {
                // increment run
                run += 1;
            }
        }
    }

    //
    //  Encode run & level arrays
    //

    {
        Ipp32u maxAbsLevel = 0;
        const AVS_ENC_ENTROPY_TABLE *pTable = &VLCEnc0_Intra;

        while (numVLCCodes--)
        {
            Ipp32u trans_coefficient;
            Ipp32u run;
            Ipp32s level;
            Ipp32u absLevel;
            Ipp32s signExtended;

            // get level & run
            run = runArray[numVLCCodes];
            level = levelArray[numVLCCodes];

            // get sign & make absolute level
            signExtended = level >> 31;
            // we use simple scheme of getting abs,
            // using one SUB and one XOR operation
            absLevel = (level ^ signExtended) - signExtended;

            // usual case
            if ((absLevel <= pTable->maxAmp) &&
                (run <= pTable->maxRun) &&
                (NA != pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)]))
            {
                // get VLC code for this pair
                trans_coefficient = pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)];
                trans_coefficient -= signExtended;

                // save trans_coefficient
                WRITE_TRACE_LOG(_enc_tst, "trans coefficient", trans_coefficient);
                PutUE(&ctx, trans_coefficient, 2);

            }
            // escape mode encoding
            else
            {
                Ipp32u escape_level_diff;
                Ipp32u MaxRun;
                Ipp32s RefAbsLevel;

                // get VLC code
                trans_coefficient = (run << 1) + 59 + (1 + signExtended);

                // save trans_coefficient
                WRITE_TRACE_LOG(_enc_tst, "trans coefficient", trans_coefficient);
                PutUE(&ctx, trans_coefficient, 2);

                // encode level difference
                MaxRun = pTable->maxRun;
                if (run > MaxRun)
                    RefAbsLevel = 1;
                else
                    RefAbsLevel = pTable->ref_level[run];

                escape_level_diff = absLevel - RefAbsLevel;

                // put difference
                WRITE_TRACE_LOG(_enc_tst, "escape level diff", escape_level_diff);
                PutUE(&ctx, escape_level_diff, 1);
            }

            WRITE_TRACE_LOG(_enc_tst, "run", run);
            WRITE_TRACE_LOG(_enc_tst, "level", level);

            // select VLC table
            {
                if (absLevel > maxAbsLevel)
                {
                    switch(absLevel)
                    {
                    case 1:
                        pTable = &VLCEnc1_Intra;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 1);
                        // update maximum level
                        maxAbsLevel = 1;
                        break;
                    case 2:
                        pTable = &VLCEnc2_Intra;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 2);
                        // update maximum level
                        maxAbsLevel = 2;
                        break;
                    case 3:
                    case 4:
                        pTable = &VLCEnc3_Intra;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 3);
                        // update maximum level
                        maxAbsLevel = 4;
                        break;
                    case 5:
                    case 6:
                    case 7:
                        pTable = &VLCEnc4_Intra;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 4);
                        // update maximum level
                        maxAbsLevel = 7;
                        break;
                    case 8:
                    case 9:
                    case 10:
                        pTable = &VLCEnc5_Intra;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 5);
                        // update maximum level
                        maxAbsLevel = 10;
                        break;
                    default:
                        pTable = &VLCEnc6_Intra;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 6);
                        // update maximum level
                        maxAbsLevel = 65535;
                        break;
                    }
                }
            }
        }

        // encode EOB
        {
            Ipp32u trans_coefficient = pTable->eobCode;

            WRITE_TRACE_LOG(_enc_tst, "trans coefficient", trans_coefficient);
            PutUE(&ctx, trans_coefficient, 2);
        }
    }

    // reflect changes
    *ppBitStream = ctx.dst;
    *pBitOffset = ctx.offset;

    return ippStsOk;

} // IppStatus avs_EncodeLumaBlockIntra_AVS_1u16s(Ipp32u **ppBitStream,

Ipp32u avs_GetSizeLumaBlockIntra_AVS_1u16s(const Ipp16s *pDstCoeffs,
                                           Ipp32u scanType)
{
    Ipp32u numVLCCodes;
    Ipp32s runArray[64];
    Ipp32s levelArray[64];
    Ipp32u blockSize = 0;

    //
    //  reorder levels
    //

    {
        Ipp32s *pScan = AVSEncScan[scanType];
        Ipp32s iIndex = 0;
        Ipp32u run;

        run = 0;
        numVLCCodes = 0;
        for (iIndex = 0; iIndex < 64; iIndex += 1)
        {
            Ipp32s level;

            // get current amplitude
            level = pDstCoeffs[pScan[iIndex]];
            if (level)
            {
                runArray[numVLCCodes] = run;
                levelArray[numVLCCodes] = level;
                numVLCCodes += 1;

                // reset run
                run = 0;
            }
            else
            {
                // increment run
                run += 1;
            }
        }
    }

    //
    //  Encode run & level arrays
    //

    {
        Ipp32u maxAbsLevel = 0;
        const AVS_ENC_ENTROPY_TABLE *pTable = &VLCEnc0_Intra;

        while (numVLCCodes--)
        {
            Ipp32u trans_coefficient;
            Ipp32u run;
            Ipp32s level;
            Ipp32u absLevel;
            Ipp32s signExtended;

            // get level & run
            run = runArray[numVLCCodes];
            level = levelArray[numVLCCodes];

            // get sign & make absolute level
            signExtended = level >> 31;
            // we use simple scheme of getting abs,
            // using one SUB and one XOR operation
            absLevel = (level ^ signExtended) - signExtended;

            // usual case
            if ((absLevel <= pTable->maxAmp) &&
                (run <= pTable->maxRun) &&
                (NA != pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)]))
            {
                // get VLC code for this pair
                trans_coefficient = pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)];
                trans_coefficient -= signExtended;

                // get size of trans_coefficient
                blockSize += GetUESize(trans_coefficient, 2);

            }
            // escape mode encoding
            else
            {
                Ipp32u escape_level_diff;
                Ipp32u MaxRun;
                Ipp32s RefAbsLevel;

                // get VLC code
                trans_coefficient = (run << 1) + 59 + (1 + signExtended);

                // get size of trans_coefficient
                blockSize += GetUESize(trans_coefficient, 2);

                // encode level difference
                MaxRun = pTable->maxRun;
                if (run > MaxRun)
                    RefAbsLevel = 1;
                else
                    RefAbsLevel = pTable->ref_level[run];

                escape_level_diff = absLevel - RefAbsLevel;

                // get size of difference
                blockSize += GetUESize(escape_level_diff, 1);
            }

            // select VLC table
            {
                if (absLevel > maxAbsLevel)
                {
                    switch(absLevel)
                    {
                    case 1:
                        pTable = &VLCEnc1_Intra;
                        // update maximum level
                        maxAbsLevel = 1;
                        break;
                    case 2:
                        pTable = &VLCEnc2_Intra;
                        // update maximum level
                        maxAbsLevel = 2;
                        break;
                    case 3:
                    case 4:
                        pTable = &VLCEnc3_Intra;
                        // update maximum level
                        maxAbsLevel = 4;
                        break;
                    case 5:
                    case 6:
                    case 7:
                        pTable = &VLCEnc4_Intra;
                        // update maximum level
                        maxAbsLevel = 7;
                        break;
                    case 8:
                    case 9:
                    case 10:
                        pTable = &VLCEnc5_Intra;
                        // update maximum level
                        maxAbsLevel = 10;
                        break;
                    default:
                        pTable = &VLCEnc6_Intra;
                        // update maximum level
                        maxAbsLevel = 65535;
                        break;
                    }
                }
            }
        }

        // encode EOB
        {
            Ipp32u trans_coefficient = pTable->eobCode;

            blockSize += GetUESize(trans_coefficient, 2);
        }
    }

    return blockSize;

} // Ipp32u avs_GetSizeLumaBlockIntra_AVS_1u16s(Ipp32u **ppBitStream,

/*
IppStatus avs_EncodeLumaBlockInter_AVS_1u16s(Ipp32u **ppBitStream,
                                             Ipp32s *pBitOffset,
                                             Ipp32s numCoeff,
                                             Ipp16s *pDstCoeffs,
                                             Ipp32u scanType)
{
    Ipp32s iIndex = 0;
    Ipp32s runArray[64];
    Ipp32s levelArray[64];
    AVS_BIT_STREAM_CONTEXT ctx = {*ppBitStream, *pBitOffset};

    //
    //  Decode run & level arrays
    //

    {
        Ipp32u maxAbsLevel = 0;
        AVS_ENTROPY_TABLE *pTable = &VLCEnc0_Inter;
        Ipp32s kOrder = 3;

        do
        {
            Ipp32u trans_coefficient;
            Ipp32u run;
            Ipp32s level;

            // get quantization coefficient
            trans_coefficient = PutUE(&ctx, kOrder);
            WRITE_TRACE_LOG(_enc_tst, "trans coefficient", trans_coefficient);

            // usual case
            if (59 > trans_coefficient)
            {
                // increment index
                run = pTable->values[trans_coefficient].run;
                if (63 < iIndex + run)
                    break;
                iIndex += run;

                // decode level
                level = pTable->values[trans_coefficient].amp;
            }
            // escape mode encoding
            else
            {
                Ipp32u escape_level_diff;
                Ipp32u MaxRun;
                Ipp32s RefAbsLevel;
                Ipp32s signExtended;

                // get difference
                escape_level_diff = PutUE(&ctx);
                WRITE_TRACE_LOG(_enc_tst, "escape level diff", escape_level_diff);

                // increment index
                run = (trans_coefficient - 59) >> 1;
                if (63 < iIndex + run)
                    break;
                iIndex += run;

                // decode level difference
                MaxRun = VLC_InterMaxRun[IPP_MIN(maxAbsLevel, 10)];
                if (run > MaxRun)
                    RefAbsLevel = 1;
                else
                    RefAbsLevel = pTable->ref_level[run];

                // create signed element
                level = RefAbsLevel + escape_level_diff;
                signExtended = -((Ipp32s) (trans_coefficient & 1));
                // we use simple scheme of sign restoration,
                // using one ADD and one XOR operation
                level = (level + signExtended) ^ signExtended;
            }

            // save level & run
            runArray[iIndex] = run;
            levelArray[iIndex] = level;

            // select VLC table
            {
                Ipp32u absLevel = (0 < level) ? (level) : (-level);

                if (absLevel > maxAbsLevel)
                {
                    switch(absLevel)
                    {
                    case 1:
                        pTable = &VLCEnc1_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 1);
                        // update maximum level
                        maxAbsLevel = 1;
                        break;
                    case 2:
                        pTable = &VLCEnc2_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 2);
                        // update maximum level
                        maxAbsLevel = 2;
                        break;
                    case 3:
                        pTable = &VLCEnc3_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 3);
                        // update maximum level
                        maxAbsLevel = 3;
                        break;
                    case 4:
                    case 5:
                    case 6:
                        pTable = &VLCEnc4_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 4);
                        // update maximum level
                        maxAbsLevel = 6;
                        break;
                    case 7:
                    case 8:
                    case 9:
                        pTable = &VLCEnc5_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 5);
                        // update maximum level
                        maxAbsLevel = 9;
                        break;
                    default:
                        pTable = &VLCEnc6_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 6);
                        // update maximum level
                        maxAbsLevel = 65535;
                        break;
                    }

                    // set k-order for others tables
                    kOrder = 2;
                }
            }

        } while (64 >= ++iIndex);
    }

    //
    //  reorder levels
    //

    {
        Ipp32s *pScan = AVSEncScan[scanType];
        Ipp32s coeffNum = -1;

        // reset block
        memset(pDstCoeffs, 0, sizeof(Ipp16s) * 64);

        // save number of coefficients
        if (0 == scanType)
            *pNumCoeff = iIndex;
        else // DEBUG: should set real value
            *pNumCoeff = 63;

        while (0 <= --iIndex)
        {
            Ipp32s run;

            // get run
            run = runArray[iIndex];
            // increment the writing index
            coeffNum += run + 1;
            // write a coefficient
            pDstCoeffs[pScan[coeffNum]] = (Ipp16s) levelArray[iIndex];
            // decrement the index
            iIndex -= run;
        }
    }

    // reflect changes
    *ppBitStream = ctx.src;
    *pBitOffset = ctx.offset;

    return ippStsOk;

} // IppStatus avs_EncodeLumaBlockInter_AVS_1u16s(Ipp32u **ppBitStream,
*/

static Ipp32u VLC_InterMaxRun[12] =
{
    25, 18, 13, 9, 6, 6, 6, 4, 4, 4, 3, 1
};

IppStatus avs_EncodeLumaBlockInter_AVS_1u16s(Ipp32u **ppBitStream,
                                             Ipp32s *pBitOffset,
                                             const Ipp16s *pDstCoeffs,
                                             Ipp32u scanType)
{
    Ipp32u numVLCCodes;
    Ipp32s runArray[64];
    Ipp32s levelArray[64];
    AVS_BIT_STREAM_CONTEXT ctx;

    // initialize bit stream
    ctx.dst = *ppBitStream;
    ctx.offset = *pBitOffset;

    //
    //  reorder levels
    //

    {
        Ipp32s *pScan = AVSEncScan[scanType];
        Ipp32s iIndex = 0;
        Ipp32u run;

        run = 0;
        numVLCCodes = 0;
        for (iIndex = 0; iIndex < 64; iIndex += 1)
        {
            Ipp32s level;

            // get current amplitude
            level = pDstCoeffs[pScan[iIndex]];
            if (level)
            {
                runArray[numVLCCodes] = run;
                levelArray[numVLCCodes] = level;
                numVLCCodes += 1;

                // reset run
                run = 0;
            }
            else
            {
                // increment run
                run += 1;
            }
        }
    }

    //
    //  Encode run & level arrays
    //

    {
        Ipp32u maxAbsLevel = 0;
        Ipp32s kOrder = 3;
        const AVS_ENC_ENTROPY_TABLE *pTable = &VLCEnc0_Inter;

        while (numVLCCodes--)
        {
            Ipp32u trans_coefficient;
            //Ipp32u temp_trans_coefficient = 0;;
            Ipp32u run;
            Ipp32s level;
            Ipp32u absLevel;
            Ipp32s signExtended;

            // get level & run
            run = runArray[numVLCCodes];
            level = levelArray[numVLCCodes];

            // get sign & make absolute level
            signExtended = level >> 31;
            // we use simple scheme of getting abs,
            // using one SUB and one XOR operation
            absLevel = (level ^ signExtended) - signExtended;

            // usual case
            if ((absLevel <= pTable->maxAmp) &&
                (run <= pTable->maxRun) &&
                (NA != pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)]))
            {
                // get VLC code for this pair
                trans_coefficient = pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)];
                trans_coefficient -= signExtended;

                // save trans_coefficient
                WRITE_TRACE_LOG(_enc_tst, "trans coefficient", trans_coefficient);
                PutUE(&ctx, trans_coefficient, kOrder);
            }
            // escape mode encoding
            else
            {
                Ipp32u escape_level_diff;
                Ipp32u MaxRun;
                Ipp32s RefAbsLevel;

                // get VLC code
                trans_coefficient = (run << 1) + 59 + (1 + signExtended);

                // save trans_coefficient
                WRITE_TRACE_LOG(_enc_tst, "trans coefficient", trans_coefficient);
                PutUE(&ctx, trans_coefficient, kOrder);

                // encode level difference
                //MaxRun = pTable->maxRun;
                MaxRun = VLC_InterMaxRun[IPP_MIN(maxAbsLevel, 10)];
                if (run > MaxRun)
                    RefAbsLevel = 1;
                else
                    RefAbsLevel = pTable->ref_level[run];

                escape_level_diff = absLevel - RefAbsLevel;

                // put difference
                WRITE_TRACE_LOG(_enc_tst, "escape level diff", escape_level_diff);
                PutUE(&ctx, escape_level_diff);
            }

            WRITE_TRACE_LOG(_enc_tst, "run", run);
            WRITE_TRACE_LOG(_enc_tst, "level", level);

            // select VLC table
            {
                if (absLevel > maxAbsLevel)
                {
                    switch(absLevel)
                    {
                    case 1:
                        pTable = &VLCEnc1_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 1);
                        // update maximum level
                        maxAbsLevel = 1;
                        break;
                    case 2:
                        pTable = &VLCEnc2_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 2);
                        // update maximum level
                        maxAbsLevel = 2;
                        break;
                    case 3:
                        pTable = &VLCEnc3_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 3);
                        // update maximum level
                        maxAbsLevel = 3;
                        break;
                    case 4:
                    case 5:
                    case 6:
                        pTable = &VLCEnc4_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 4);
                        // update maximum level
                        maxAbsLevel = 6;
                        break;
                    case 7:
                    case 8:
                    case 9:
                        pTable = &VLCEnc5_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 5);
                        // update maximum level
                        maxAbsLevel = 9;
                        break;
                    default:
                        pTable = &VLCEnc6_Inter;
                        WRITE_TRACE_LOG(_enc_tst, "selected table", 6);
                        // update maximum level
                        maxAbsLevel = 65535;
                        break;
                    }
                }
                // set k-order for others tables
                kOrder = 2;
            }
        }

        // encode EOB
        {
            Ipp32u trans_coefficient = pTable->eobCode;

            WRITE_TRACE_LOG(_enc_tst, "trans coefficient", trans_coefficient);
            //PutUE(&ctx, trans_coefficient, 2);
            PutUE(&ctx, trans_coefficient, kOrder);
        }
    }

    // reflect changes
    *ppBitStream = ctx.dst;
    *pBitOffset = ctx.offset;

    return ippStsOk;

} // IppStatus avs_EncodeLumaBlockInter_AVS_1u16s(Ipp32u **ppBitStream,

Ipp32u avs_GetSizeLumaBlockInter_AVS_1u16s(const Ipp16s *pDstCoeffs,
                                           Ipp32u scanType)
{
    Ipp32u numVLCCodes;
    Ipp32s runArray[64];
    Ipp32s levelArray[64];
    Ipp32u blockSize = 0;

    //
    //  reorder levels
    //

    {
        Ipp32s *pScan = AVSEncScan[scanType];
        Ipp32s iIndex = 0;
        Ipp32u run;

        run = 0;
        numVLCCodes = 0;
        for (iIndex = 0; iIndex < 64; iIndex += 1)
        {
            Ipp32s level;

            // get current amplitude
            level = pDstCoeffs[pScan[iIndex]];
            if (level)
            {
                runArray[numVLCCodes] = run;
                levelArray[numVLCCodes] = level;
                numVLCCodes += 1;

                // reset run
                run = 0;
            }
            else
            {
                // increment run
                run += 1;
            }
        }
    }

    //
    //  Encode run & level arrays
    //

    {
        Ipp32u maxAbsLevel = 0;
        Ipp32s kOrder = 3;
        const AVS_ENC_ENTROPY_TABLE *pTable = &VLCEnc0_Inter;

        while (numVLCCodes--)
        {
            Ipp32u trans_coefficient;
            Ipp32u run;
            Ipp32s level;
            Ipp32u absLevel;
            Ipp32s signExtended;

            // get level & run
            run = runArray[numVLCCodes];
            level = levelArray[numVLCCodes];

            // get sign & make absolute level
            signExtended = level >> 31;
            // we use simple scheme of getting abs,
            // using one SUB and one XOR operation
            absLevel = (level ^ signExtended) - signExtended;

            // usual case
            if ((absLevel <= pTable->maxAmp) &&
                (run <= pTable->maxRun) &&
                (NA != pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)]))
            {
                // get VLC code for this pair
                trans_coefficient = pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)];
                trans_coefficient -= signExtended;

                // get size of trans_coefficient
                blockSize += GetUESize(trans_coefficient, kOrder);

            }
            // escape mode encoding
            else
            {
                Ipp32u escape_level_diff;
                Ipp32u MaxRun;
                Ipp32s RefAbsLevel;

                // get VLC code
                trans_coefficient = (run << 1) + 59 + (1 + signExtended);

                // get size of trans_coefficient
                blockSize += GetUESize(trans_coefficient, kOrder);

                // encode level difference
                MaxRun = pTable->maxRun;
                if (run > MaxRun)
                    RefAbsLevel = 1;
                else
                    RefAbsLevel = pTable->ref_level[run];

                escape_level_diff = absLevel - RefAbsLevel;

                // get size of difference
                //blockSize += GetUESize(escape_level_diff, 1);
                blockSize += GetUESize(escape_level_diff);
            }

            // select VLC table
            {
                if (absLevel > maxAbsLevel)
                {
                    switch(absLevel)
                    {
                    case 1:
                        pTable = &VLCEnc1_Inter;
                        // update maximum level
                        maxAbsLevel = 1;
                        break;
                    case 2:
                        pTable = &VLCEnc2_Inter;
                        // update maximum level
                        maxAbsLevel = 2;
                        break;
                    case 3:
                        pTable = &VLCEnc3_Inter;
                        // update maximum level
                        maxAbsLevel = 3;
                        break;
                    case 4:
                    case 5:
                    case 6:
                        pTable = &VLCEnc4_Inter;
                        // update maximum level
                        maxAbsLevel = 6;
                        break;
                    case 7:
                    case 8:
                    case 9:
                        pTable = &VLCEnc5_Inter;
                        // update maximum level
                        maxAbsLevel = 9;
                        break;
                    default:
                        pTable = &VLCEnc6_Inter;
                        // update maximum level
                        maxAbsLevel = 65535;
                        break;
                    }
                }
                // set k-order for others tables
                kOrder = 2;
            }
        }

        // encode EOB
        {
            Ipp32u trans_coefficient = pTable->eobCode;

            blockSize += GetUESize(trans_coefficient, kOrder);
        }
    }

    return blockSize;

} // Ipp32u avs_GetSizeLumaBlockInter_AVS_1u16s(Ipp32u **ppBitStream,

IppStatus avs_EncodeChromaBlock_AVS_1u16s(Ipp32u **ppBitStream,
                                          Ipp32s *pBitOffset,
                                          const Ipp16s *pDstCoeffs,
                                          Ipp32u scanType)
{
    Ipp32u numVLCCodes;
    Ipp32s runArray[64];
    Ipp32s levelArray[64];
    AVS_BIT_STREAM_CONTEXT ctx;

    // initialize bit stream
    ctx.dst = *ppBitStream;
    ctx.offset = *pBitOffset;

    //
    //  reorder levels
    //

    {
        Ipp32s *pScan = AVSEncScan[scanType];
        Ipp32s iIndex = 0;
        Ipp32u run;

        run = 0;
        numVLCCodes = 0;
        for (iIndex = 0; iIndex < 64; iIndex += 1)
        {
            Ipp32s level;

            // get current amplitude
            level = pDstCoeffs[pScan[iIndex]];
            if (level)
            {
                runArray[numVLCCodes] = run;
                levelArray[numVLCCodes] = level;
                numVLCCodes += 1;

                // reset run
                run = 0;
            }
            else
            {
                // increment run
                run += 1;
            }
        }
    }

    //
    //  Encode run & level arrays
    //

    {
        Ipp32u maxAbsLevel = 0;
        const AVS_ENC_ENTROPY_TABLE *pTable = &VLCEnc0_Chroma;
        Ipp32s kOrder = 2;

        while (numVLCCodes--)
        {
            Ipp32u trans_coefficient;
            Ipp32u run;
            Ipp32s level;
            Ipp32u absLevel;
            Ipp32s signExtended;

            // get level & run
            run = runArray[numVLCCodes];
            level = levelArray[numVLCCodes];

            // get sign & make absolute level
            signExtended = level >> 31;
            // we use simple scheme of getting abs,
            // using one SUB and one XOR operation
            absLevel = (level ^ signExtended) - signExtended;

            // usual case
            if ((absLevel <= pTable->maxAmp) &&
                (run <= pTable->maxRun) &&
                (NA != pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)]))
            {
                // get VLC code for this pair
                trans_coefficient = pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)];
                trans_coefficient -= signExtended;

                // save quantization coefficient
                WRITE_TRACE_LOG(_enc_tst, "trans coefficient chroma", trans_coefficient);
                PutUE(&ctx, trans_coefficient, kOrder);
            }
            // escape mode encoding
            else
            {
                Ipp32u escape_level_diff;
                Ipp32u MaxRun;
                Ipp32s RefAbsLevel;

                // get VLC code
                trans_coefficient = (run << 1) + 59 + (1 + signExtended);

                // save quantization coefficient
                WRITE_TRACE_LOG(_enc_tst, "trans coefficient chroma", trans_coefficient);
                PutUE(&ctx, trans_coefficient, kOrder);

                // encode level difference
                MaxRun = pTable->maxRun;
                if (run > MaxRun)
                    RefAbsLevel = 1;
                else
                    RefAbsLevel = pTable->ref_level[run];

                escape_level_diff = absLevel - RefAbsLevel;

                // save trans_coefficient
                WRITE_TRACE_LOG(_enc_tst, "escape level diff chroma", escape_level_diff);
                PutUE(&ctx, escape_level_diff);
            }

            WRITE_TRACE_LOG(_enc_tst, "run", run);
            WRITE_TRACE_LOG(_enc_tst, "level", level);

            // select VLC table
            {
                if (absLevel > maxAbsLevel)
                {
                    switch(absLevel)
                    {
                    case 1:
                        pTable = &VLCEnc1_Chroma;
                        WRITE_TRACE_LOG(_enc_tst, "selected chroma table", 1);
                        kOrder = 0;
                        // update maximum level
                        maxAbsLevel = 1;
                        break;
                    case 2:
                        pTable = &VLCEnc2_Chroma;
                        WRITE_TRACE_LOG(_enc_tst, "selected chroma table", 2);
                        kOrder = 1;
                        // update maximum level
                        maxAbsLevel = 2;
                        break;
                    case 3:
                    case 4:
                        pTable = &VLCEnc3_Chroma;
                        WRITE_TRACE_LOG(_enc_tst, "selected chroma table", 3);
                        kOrder = 1;
                        // update maximum level
                        maxAbsLevel = 4;
                        break;
                    default:
                        pTable = &VLCEnc4_Chroma;
                        WRITE_TRACE_LOG(_enc_tst, "selected chroma table", 4);
                        kOrder = 0;
                        // update maximum level
                        maxAbsLevel = 65535;
                        break;
                    }
                }
            }
        }

        // encode EOB
        {
            Ipp32u trans_coefficient = pTable->eobCode;

            WRITE_TRACE_LOG(_enc_tst, "trans coefficient chroma", trans_coefficient);
            PutUE(&ctx, trans_coefficient, kOrder);
        }
    }

    // reflect changes
    *ppBitStream = ctx.dst;
    *pBitOffset = ctx.offset;

    return ippStsOk;

} // IppStatus avs_EncodeChromaBlock_AVS_1u16s(Ipp32u **ppBitStream,

Ipp32u avs_GetSizeChromaBlock_AVS_1u16s(const Ipp16s *pDstCoeffs,
                                        Ipp32u scanType)
{
    Ipp32u numVLCCodes;
    Ipp32s runArray[64];
    Ipp32s levelArray[64];
    Ipp32u blockSize = 0;

    //
    //  reorder levels
    //

    {
        Ipp32s *pScan = AVSEncScan[scanType];
        Ipp32s iIndex = 0;
        Ipp32u run;

        run = 0;
        numVLCCodes = 0;
        for (iIndex = 0; iIndex < 64; iIndex += 1)
        {
            Ipp32s level;

            // get current amplitude
            level = pDstCoeffs[pScan[iIndex]];
            if (level)
            {
                runArray[numVLCCodes] = run;
                levelArray[numVLCCodes] = level;
                numVLCCodes += 1;

                // reset run
                run = 0;
            }
            else
            {
                // increment run
                run += 1;
            }
        }
    }

    //
    //  Encode run & level arrays
    //

    {
        Ipp32u maxAbsLevel = 0;
        const AVS_ENC_ENTROPY_TABLE *pTable = &VLCEnc0_Chroma;
        Ipp32s kOrder = 2;

        while (numVLCCodes--)
        {
            Ipp32u trans_coefficient;
            Ipp32u run;
            Ipp32s level;
            Ipp32u absLevel;
            Ipp32s signExtended;

            // get level & run
            run = runArray[numVLCCodes];
            level = levelArray[numVLCCodes];

            // get sign & make absolute level
            signExtended = level >> 31;
            // we use simple scheme of getting abs,
            // using one SUB and one XOR operation
            absLevel = (level ^ signExtended) - signExtended;

            // usual case
            if ((absLevel <= pTable->maxAmp) &&
                (run <= pTable->maxRun) &&
                (NA != pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)]))
            {
                // get VLC code for this pair
                trans_coefficient = pTable->vlcCode[run * pTable->maxAmp + (absLevel - 1)];
                trans_coefficient -= signExtended;

                // get size of quantization coefficient
                blockSize += GetUESize(trans_coefficient, kOrder);
            }
            // escape mode encoding
            else
            {
                Ipp32u escape_level_diff;
                Ipp32u MaxRun;
                Ipp32s RefAbsLevel;

                // get VLC code
                trans_coefficient = (run << 1) + 59 + (1 + signExtended);

                // get size of quantization coefficient
                blockSize += GetUESize(trans_coefficient, kOrder);

                // encode level difference
                MaxRun = pTable->maxRun;
                if (run > MaxRun)
                    RefAbsLevel = 1;
                else
                    RefAbsLevel = pTable->ref_level[run];

                escape_level_diff = absLevel - RefAbsLevel;

                // get size of level difference
                blockSize += GetUESize(escape_level_diff);
            }

            // select VLC table
            {
                if (absLevel > maxAbsLevel)
                {
                    switch(absLevel)
                    {
                    case 1:
                        pTable = &VLCEnc1_Chroma;
                        kOrder = 0;
                        // update maximum level
                        maxAbsLevel = 1;
                        break;
                    case 2:
                        pTable = &VLCEnc2_Chroma;
                        kOrder = 1;
                        // update maximum level
                        maxAbsLevel = 2;
                        break;
                    case 3:
                    case 4:
                        pTable = &VLCEnc3_Chroma;
                        kOrder = 1;
                        // update maximum level
                        maxAbsLevel = 4;
                        break;
                    default:
                        pTable = &VLCEnc4_Chroma;
                        kOrder = 0;
                        // update maximum level
                        maxAbsLevel = 65535;
                        break;
                    }
                }
            }
        }

        // encode EOB
        {
            Ipp32u trans_coefficient = pTable->eobCode;

            blockSize += GetUESize(trans_coefficient, kOrder);
        }
    }

    return blockSize;

} // Ipp32u avs_GetSizeChromaBlock_AVS_1u16s(const Ipp16s *pDstCoeffs,

} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_ENCODER)
